import pandas as pd
import numpy as np
from pulp import *

# 示例数据：应聘者信息，包括笔试成绩、面试评分和意愿
applicants_data = {
    '应聘者': [f'{i + 1}' for i in range(16)],
    '笔试成绩': [290, 288, 288, 285, 283, 283, 280, 280, 280, 280, 278, 277, 275, 275, 274, 273],
    '知识面评分': [4, 4, 3, 4, 3, 3, 4, 3, 3, 1, 1, 4, 3, 1, 4, 3],
    '理解能力评分': [4, 3, 4, 3, 4, 1, 3, 4, 3, 3, 2, 3, 2, 3, 3, 4],
    '应变能力评分': [3, 4, 1, 3, 3, 4, 2, 4, 4, 4, 3, 2, 1, 4, 2, 3],
    '表达能力评分': [3, 2, 2, 3, 2, 3, 3, 2, 3, 2, 4, 4, 4, 3, 3, 2],
    '意愿1': ['行政管理', '技术管理', '行政执法', '公共事业'] * 4,
    '意愿2': ['行政管理', '技术管理', '行政执法', '公共事业'] * 4
}

# 示例数据：用人部门信息，包括各项要求
# 优中差 转换为 3, 2, 1；大中小 转换为 3, 2, 1；多中少 转换为 3, 2, 1
positions_data = {
    '用人部门': [f'部门{j + 1}' for j in range(7)],
    '知识面要求': [3, 4, 4, 2, 2, 2, 2],
    '理解能力要求': [4, 3, 3, 2, 2, 3, 3],
    '应变能力要求': [2, 3, 3, 4, 4, 3, 3],
    '表达能力要求': [4, 2, 2, 4, 4, 4, 4],
    '福利待遇': [3, 2, 1, 3, 2, 2, 2],
    '工作条件': [3, 3, 2, 1, 3, 2, 1],
    '劳动强度': [2, 2, 1, 3, 1, 2, 1],
    '晋升机会': [3, 2, 2, 3, 1, 3, 2],
    '深造机会': [2, 3, 2, 1, 3, 2, 3]
}

# 将应聘者数据和用人部门数据转换为DataFrame
df_applicants = pd.DataFrame(applicants_data)
df_positions = pd.DataFrame(positions_data)

# 将笔试成绩转换为 1-4 的评分
def convert_exam_score(score):
    min_score, max_score = 273, 290  # 假设笔试成绩范围
    score = (score - min_score) / (max_score - min_score)  # 归一化到0-1
    return 1 + 2 * score  # 映射到1-3的评分

# 计算匹配度矩阵的函数
def calculate_match(applicant, position, alpha=0.5, beta=0.1, gamma=0.2):
    # 笔试成绩转换为 1-3 的评分
    exam_score = convert_exam_score(applicant['笔试成绩'])

    # 笔试成绩和面试评分的匹配度计算
    match = (
            alpha * exam_score -
            (1 - alpha) * (
                    abs(applicant['知识面评分'] - position['知识面要求']) +
                    abs(applicant['理解能力评分'] - position['理解能力要求']) +
                    abs(applicant['应变能力评分'] - position['应变能力要求']) +
                    abs(applicant['表达能力评分'] - position['表达能力要求'])
            )
    )
    # 如果应聘者的意愿与部门匹配，提高匹配度
    if position.name in [applicant['意愿1'], applicant['意愿2']]:
        match += beta * 100  # 提高意愿匹配度，可以调整这个值

    # 引入福利待遇等指标
    match += gamma * (
            position['福利待遇'] +
            position['工作条件'] +
            position['劳动强度'] +
            position['晋升机会'] +
            position['深造机会']
    )
    return match

# 创建匹配度矩阵
def create_match_matrix(beta, gamma):
    match_matrix = []
    for _, applicant in df_applicants.iterrows():
        matches = []
        for _, position in df_positions.iterrows():
            match = calculate_match(applicant, position, beta=beta, gamma=gamma)
            matches.append(match)
        match_matrix.append(matches)
    return pd.DataFrame(match_matrix, columns=df_positions['用人部门'], index=df_applicants['应聘者'])

# 创建LP问题
def solve_lp_problem(beta, gamma):
    df_match = create_match_matrix(beta, gamma)

    # 创建LP问题
    prob = LpProblem("RecruitmentProblem", LpMaximize)

    # 创建决策变量，表示应聘者是否被分配到某个部门
    x = LpVariable.dicts("x", (df_applicants['应聘者'], df_positions['用人部门']), cat='Binary')

    # 目标函数：最大化匹配度总和
    prob += lpSum([df_match.loc[i, j] * x[i][j] for i in df_applicants['应聘者'] for j in df_positions['用人部门']])

    # 约束条件
    # 每个应聘者最多分配到一个部门
    for i in df_applicants['应聘者']:
        prob += lpSum([x[i][j] for j in df_positions['用人部门']]) <= 1

    # 每个部门至少分配一个应聘者
    for j in df_positions['用人部门']:
        prob += lpSum([x[i][j] for i in df_applicants['应聘者']]) >= 1

    # 总共选出8个应聘者
    prob += lpSum([x[i][j] for i in df_applicants['应聘者'] for j in df_positions['用人部门']]) == 8

    # 求解问题
    prob.solve()

    # 计算目标值
    total_match_score = value(prob.objective)
    return total_match_score, df_match

# 网格搜索
beta_values = [0.1, 0.125, 0.15, 0.175, 0.2, 0.25]
gamma_values = [0.1, 0.125, 0.15, 0.175, 0.2, 0.25]

# 存储最佳参数和对应的匹配度
best_score = -np.inf
best_params = (None, None)

# 遍历所有组合
for beta in beta_values:
    for gamma in gamma_values:
        score, _ = solve_lp_problem(beta, gamma)
        print(f"beta: {beta}, gamma: {gamma}, score: {score}")
        if score > best_score:
            best_score = score
            best_params = (beta, gamma)

print(f"最佳参数: beta = {best_params[0]}, gamma = {best_params[1]}, 匹配度总和: {best_score}")

# 使用最佳参数值进行匹配
df_match = create_match_matrix(best_params[0], best_params[1])

# 创建LP问题
prob = LpProblem("RecruitmentProblem", LpMaximize)

# 创建决策变量，表示应聘者是否被分配到某个部门
x = LpVariable.dicts("x", (df_applicants['应聘者'], df_positions['用人部门']), cat='Binary')

# 目标函数：最大化匹配度总和
prob += lpSum([df_match.loc[i, j] * x[i][j] for i in df_applicants['应聘者'] for j in df_positions['用人部门']])

# 约束条件
# 每个应聘者最多分配到一个部门
for i in df_applicants['应聘者']:
    prob += lpSum([x[i][j] for j in df_positions['用人部门']]) <= 1

# 每个部门至少分配一个应聘者
for j in df_positions['用人部门']:
    prob += lpSum([x[i][j] for i in df_applicants['应聘者']]) >= 1

# 总共选出8个应聘者
prob += lpSum([x[i][j] for i in df_applicants['应聘者'] for j in df_positions['用人部门']]) == 8

# 求解问题
prob.solve()

# 输出最佳匹配度矩阵
print("最佳匹配度矩阵:")
print(df_match)

# 输出最佳匹配结果
assignments = []
for v in prob.variables():
    if v.varValue == 1:
        parts = v.name.split('_')
        applicant = parts[1]
        position = parts[2]
        assignments.append((applicant, position))

assignments_df = pd.DataFrame(assignments, columns=['应聘者', '用人部门'])
print(assignments_df)
